#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "theme.h"

// #define time_cost(t0, t1) ((t1.tv_sec-t0.tv_sec)*1000000+(t1.tv_usec-t0.tv_usec))

#define PATH_MAX    (4096 + 1)
#define HOST_MAX    PATH_MAX
#define VENV_MAX    (256 + 1)
#define BRANCH_MAX  (256 + 1)
#define PS1_MAX     PATH_MAX
#define STATUS_MAX  64

#define head(str, n)    ({str[n] = '\0';})
#define add(dst, src)   ({if (*dst) strcat(dst, src);})

static int errnum = 0;

char *get_fishpwd(const char *pwd, char *home)
{
    static char fish[PATH_MAX] = {0};
    int index = 0;

    if (strcmp(pwd, home) == 0)
        return "~";
    if (strcmp(pwd, "/") == 0)
        return "/";

    if (strstr(pwd, home) != NULL) { // /home/<user>/abc --> ~/abc
        pwd += strlen(home); // p --> /abc
        fish[index++] = '~';
    }

    const char *base = pwd; // basename of pwd
    while ((pwd = strchr(pwd, '/')) != NULL) {
        fish[index++] = *pwd++; // '/'
        fish[index++] = *pwd++; // 'a'
        base = pwd; 
    }
    strcat(fish, base);

    return fish;
}

char *get_branch(const char *pwd)
{
    static char path[PATH_MAX + BRANCH_MAX] = {0};
    static char branch[BRANCH_MAX] = {0};
    strcat(path, pwd);
    strcat(path, "/"); // pwd: /home/user/xxx, path: /home/user/xxx/

    char *p = NULL; // iter for path, point to '/'
    while ((p = strrchr(path, '/')) != NULL) {
        strcpy(p + 1, ".git"); // path: /home/user/xxx/.git/HEAD
        if (access(path, X_OK) != 0) { // git HEAD not readable!
            *p = '\0';
            continue;
        }
        strcat(path, "/HEAD");

        int fd = 0;
        if ((fd = open(path, O_RDONLY)) < 0) {
            errnum = -12;
            goto err;
        }

        if (read(fd, branch, sizeof(branch)) > 0) { // HEAD: "ref: refs/heads/master\n", is "\r\n" on windows?
            char *b = NULL; // iter for branch
            if ((b = strrchr(branch, '\n')) != NULL) // remove "\n"
                *b = '\0';
            /* "ref: refs/heads/master"
             *               b ^      */
            if ((b = strrchr(branch, '/' )) == NULL) {
                errnum = -13;
                goto err;
            }

            strcpy(branch, b + 1);
            return branch; // done!
        }
        goto err;
    }

err:
    branch[0]= '\0';
    return branch; // git HEAD not exist
}

char *get_venv(void)
{
    static char venv[VENV_MAX] = {0};
    char *env = getenv("VIRTUAL_ENV");
    if (env != NULL) {
        char *c = strrchr(env, '/');
        if (c != NULL)
            strcpy(venv, c + 1); 
    }
    return venv;
}

char *get_host(void)
{
    static char host[HOST_MAX] = {0};
    char *env = getenv("CORKLINE_HOST");
    if (env != NULL)
        return env;
    gethostname(host, sizeof(host));
    return host;
}

char *get_pwd(void)
{
    static char pwd[4096 + 1] = {0};
    getcwd(pwd, sizeof(pwd));
    return pwd;
}

char *get_ps1(char *status)
{
    static char ps1[PS1_MAX] = {0};
    struct passwd *pw = getpwuid(getuid());

    const char *pwd = get_pwd();
    char *user = pw->pw_name;
    char *home = pw->pw_dir;
    int   uid  = pw->pw_uid;
    char *host = get_host();
    char *fish = get_fishpwd(pwd, home);
    char *venv = get_venv();
    char *branch = get_branch(pwd);

    head(branch, 2);
    add(branch, " ");
    head(venv, 2);
    add(venv, " ");

    status = STATUS(status);

    sprintf(ps1, THEME(uid), status, user, host, fish, branch, venv);
    return ps1;
}

int main(int argc, char *argv[])
{
    char *status = NULL;

    if (argc == 2) {
        if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
            printf("help\n");
            return 0;
        } else {
            status = argv[1];
        }
    } else if (argc > 2) {
        errnum = -1;
    }

    char *ps1 = get_ps1(status);
    printf("%s\n", ps1);
    return -1;
}
